
#include "socket.h"
#include "heap.h"


//local datas
static uint32 sSocketCounter;
static int32 sSocketSelectTimer;
static SOCKET sSocketsPool[SOCKET_MAX_COUNT];

#ifdef LOW_MEMORY
#define MSG_ITEM_NUM   5
#else
#define MSG_ITEM_NUM   10
#endif
static NETMSG sNetMsgMem[MSG_ITEM_NUM];
static HBLOCKHEAP NetMsgHeap;

//////////////////////////////////////////////////////////////////////////////////

static int32 Socket_SendLoop(PSOCKET socket);
static int32 Socket_RecvLoop(PSOCKET socket);

static PSOCKET Socket_Allocate(void)
{
	int i;

	//get a unused socket from sockets pool
	for(i = 0; i < SOCKET_MAX_COUNT; i++)
	{
		if(sSocketsPool[i].sd < 0) 
			return &sSocketsPool[i];
	}

	return NULL;
}

static void Socket_Select(int32 data)
{
	int32 i, ret;
	PSOCKET socket;
	
	for(i = 0; i < SOCKET_MAX_COUNT; i++)
	{
		socket = &sSocketsPool[i];
		if(socket->sd < 0 
			|| (socket->protocol == SOCKPROTO_TCP && socket->state == SOCKSTAT_CREATED) 
			|| socket->state == SOCKSTAT_CLOSED) 
			continue;

		/*
		* for socket can connect in unblock way, so here we should check the unconnected socket state.
		* if connected notify the user, close it when someting error happen.
		*/
		if(socket->state == SOCKSTAT_CONNECTING)
		{
			if((ret = mrc_getSocketState(socket->sd)) == MR_SUCCESS)
			{
				socket->state = SOCKSTAT_CONNECTED;

				//is the last sentence, so do not care about the return from event handler
				socket->fnEvent(socket, SOCKEVT_CONNECTED, NULL);				
			}else if(ret == MR_FAILED){
				Socket_Close(socket, SOCKEVT_CONNECTFAILED);
			}
			continue;
		}

		/** send the data */
		//if(socket->fnSend(socket) == MR_SUCCESS)
		if(Socket_SendLoop(socket) == MR_SUCCESS)
		{
			/** receive the data */
			//socket->fnRecv(socket);
			Socket_RecvLoop(socket);
		}
	}
}

static int32 Socket_SendLoop(PSOCKET socket)
{
	if(socket->sendList && socket->sendList->dataLen)
	{
		int32 ret;
		PNETMSG pMsg = socket->sendList;

#ifdef SOCKET_UDP_ENABLE
		if(socket->protocol == SOCKPROTO_TCP)
#endif
			ret = mrc_send(socket->sd, 
				(char*)(pMsg->buf + pMsg->sentLen), 
				pMsg->dataLen - pMsg->sentLen);
#ifdef SOCKET_UDP_ENABLE
		else 
			ret = mrc_sendto(socket->sd, 
				(char*)(pMsg->buf + pMsg->sentLen), 
				pMsg->dataLen - pMsg->sentLen,
				pMsg->ip, pMsg->port);
#endif
			
#if 0
if(ret > 0)
{
int32 fd = mrc_open("socsend.buf", MR_FILE_WRONLY | MR_FILE_CREATE);
mrc_seek(fd, 0, MR_SEEK_END);
mrc_write(fd, "\n####\n", strlen("\n####\n"));
mrc_write(fd, pMsg->buf + pMsg->sentLen, ret);
mrc_close(fd);
}
#endif
		if(ret > 0)
		{
			pMsg->sentLen += ret;
			/** This message has sent, tell its sender */
			if(pMsg->sentLen >= pMsg->dataLen)
			{
				socket->sendList = socket->sendList->next;
				socket->fnEvent(socket, SOCKEVT_DATASENT, pMsg->buf);
				BlockHeap_Free(&NetMsgHeap, pMsg);
				return MR_SUCCESS;
			}
		}
		else if(ret == 0)
			return MR_SUCCESS;
		else if(ret < 0)
		{
			Socket_Close(socket, SOCKEVT_ERROR);
			return MR_FAILED;
		}
	}
	return MR_SUCCESS;
}

static int32 Socket_RecvLoop(PSOCKET socket)
{
		int32 ret = 0, recvLen;

		recvLen = (socket->recvBuf->maxLen - socket->recvBuf->len > ONCE_RECV_LEN) ? 
			ONCE_RECV_LEN : (socket->recvBuf->maxLen - socket->recvBuf->len);
		if(recvLen)
		{
#ifdef SOCKET_UDP_ENABLE
			if(socket->protocol == SOCKPROTO_TCP)
#endif
				ret = mrc_recv(socket->sd, (char*)(socket->recvBuf->buf + socket->recvBuf->len), recvLen);
#ifdef SOCKET_UDP_ENABLE
			else 
			{
				int32 ip;
				uint16 port;
				ret = mrc_recvfrom(socket->sd, (char*)(socket->recvBuf->buf + socket->recvBuf->len), recvLen, &ip, &port);
			}
#endif
			
#if 0
if(ret > 0)
{
	int32 flag = MR_FILE_WRONLY;
	int32 fd;
	if ( mrc_fileState("socrcv.buf") !=MR_IS_FILE)
		flag = flag|MR_FILE_CREATE;
	fd = mrc_open("socrcv.buf", flag);
	mrc_seek(fd, 0, MR_SEEK_END);
	//mrc_write(fd, "\n####\n", strlen("\n####\n"));
	mrc_write(fd, socket->recvBuf->buf + socket->recvBuf->len, ret);
	mrc_close(fd);
}
#endif
			if(ret < 0)
			{
				Socket_Close(socket, SOCKEVT_ERROR);
				return MR_FAILED;
			}
			socket->recvBuf->len += ret;
		}
		if(socket->recvBuf->len)
		{
			int32 ret;
			SOCKEVTDATA recvData = {0};
			recvData.buffer = socket->recvBuf->buf;
			recvData.size = socket->recvBuf->len;
			ret = socket->fnEvent(socket, SOCKEVT_RECVDATA, &recvData);
			if(ret > 0 && socket->recvBuf)
			{
				memmove(socket->recvBuf->buf, socket->recvBuf->buf+ret, socket->recvBuf->len-ret);
				socket->recvBuf->len -= ret;
			} 
		}
		
	return MR_SUCCESS;
}

void Socket_Initialize(void)
{
	int i ;
	
	sSocketCounter = 0;
	for(i = 0; i < SOCKET_MAX_COUNT; i++)
		sSocketsPool[i].sd = -1;
	NetMsgHeap = BlockHeap_Initialize(sNetMsgMem, sizeof(sNetMsgMem), sizeof(NETMSG));
}


void Socket_Terminate(void)
{
	Socket_CloseAll(SOCKEVT_CLOSED);
}


PSOCKET Socket_Create(SOCKPROTO protocol, FN_SOCKEVENT fnEvent, int32 userdata)
{
	PSOCKET socket = Socket_Allocate();
	if(!socket) return NULL;

	socket->state = SOCKSTAT_CREATED;
	socket->protocol = protocol;
	socket->userdata = userdata;
	socket->fnEvent = fnEvent;
	socket->sendList = NULL;
	socket->recvBuf = malloc(sizeof(RECVBUF));
	if(!socket->recvBuf)
		return NULL;
	memset(socket->recvBuf, 0, sizeof(RECVBUF));
	socket->recvBuf->maxLen = SOCKET_RECVBUFFER_SIZE;

	if(protocol == SOCKPROTO_TCP)
	{
		//socket->fnSend = Socket_Send;
		//socket->fnRecv = Socket_Recv;
		socket->sd = mrc_socket(MR_SOCK_STREAM, MR_IPPROTO_TCP);
	}
#ifdef SOCKET_UDP_ENABLE
	else{
		//socket->fnRecv = Socket_SendTo;
		//socket->fnRecv = Socket_RecvFrom;
		socket->sd = mrc_socket(MR_SOCK_DGRAM, MR_IPPROTO_UDP);
	}
#endif
	
	if(socket->sd < 0)
	{
		socket->state = SOCKSTAT_CLOSED;
		free(socket->recvBuf);
		socket->recvBuf = NULL;
		//is the last sentence, so do not care about the return from event handler
		fnEvent(socket, SOCKEVT_ERROR, NULL);
		return NULL;
	}
	/*
	* sSocketCounter to track the socket count in the system.
	* when the value changed 0 => 1 the select timer will be created and started.
	* when the value changed 1 => 0 the select timer will be stoped.
	*/
	if(++sSocketCounter == 1);
	{
		if(sSocketSelectTimer == 0)
			 sSocketSelectTimer = mrc_timerCreate();

		mrc_timerStart(sSocketSelectTimer, SOCKET_SELECT_INTERVAL, 0, Socket_Select, TRUE);
	}
	return socket;
}


int32 Socket_Close(PSOCKET socket, SOCKEVENT evt)
{	
	if(socket && socket->sd >= 0)
	{
		PNETMSG pMsg = socket->sendList;
		if(--sSocketCounter == 0)
			mrc_timerStop(sSocketSelectTimer);
		mrc_closeSocket(socket->sd);
		
		socket->sd = -1;
		socket->state = SOCKSTAT_CLOSED;
		if(socket->recvBuf)
			free(socket->recvBuf);
		socket->recvBuf = NULL;

		//is the last sentence, so do not care about the return from event handler
		socket->fnEvent(socket, evt, NULL);
		while(pMsg)
		{
			PNETMSG pTmp = pMsg->next;
			BlockHeap_Free(&NetMsgHeap, pMsg);
			pMsg = pTmp;
		}
		socket->sendList = NULL;
		return MR_SUCCESS;
	}

	return MR_FAILED;
}


int32 Socket_Connect(PSOCKET socket, uint32 ip, uint16 port, int block)
{
	int32 r;

	if(!socket || socket->sd < 0) 
		return FALSE;
	
#ifdef SDK_MOD
	//for the simulator not support the UNBLOCK way
	if((r = mrc_connect(socket->sd, (int32)ip, (uint16)port, MR_SOCKET_BLOCK)) == MR_FAILED)	
#else
	if((r = mrc_connect(socket->sd, (int32)ip, (uint16)port, block? MR_SOCKET_BLOCK : MR_SOCKET_NONBLOCK)) == MR_FAILED)		
#endif
	{
		Socket_Close(socket, SOCKEVT_CONNECTFAILED);
		return MR_FAILED;
	}

	if(r == MR_SUCCESS)
	{
		socket->state = SOCKSTAT_CONNECTED;

		//is the last sentence, so do not care about the return from event handler
		socket->fnEvent(socket, SOCKEVT_CONNECTED, NULL);
	}else{
		socket->state = SOCKSTAT_CONNECTING;
	}

	return MR_SUCCESS;
}

#ifdef SOCKET_UDP_ENABLE
int32 Socket_SendTo(PSOCKET socket, uint8* data, uint32 size, int32 ip, uint16 port)
#else
int32 Socket_Send(PSOCKET socket, uint8* data, uint32 size)
#endif
{
	if(socket && data)
	{
		PNETMSG ptr = BlockHeap_Alloc(&NetMsgHeap);
		if(!ptr)
		{
			return MR_FAILED;
		}

		ptr->buf = data;
		ptr->dataLen = size;
		//ptr->privData = privData;
		ptr->sentLen = 0;
		ptr->next = NULL;
#ifdef SOCKET_UDP_ENABLE
		ptr->ip = ip;
		ptr->port = port;
#endif
		
		if(socket->sendList == NULL)
			socket->sendList = ptr;
		else {
			NETMSG* tmp = socket->sendList;
			while(tmp->next != NULL)
				tmp = tmp->next;
			tmp->next = ptr;
		}
		return MR_SUCCESS;
	}
	return MR_FAILED;
}

int32 Socket_IsConnected(PSOCKET socket)
{
	return (socket && socket->sd >= 0 
		&& (socket->protocol == SOCKPROTO_UDP || socket->state == SOCKSTAT_CONNECTED));
}


void Socket_CloseAll(SOCKEVENT evt)
{
	int i;

	for(i = 0; i < SOCKET_MAX_COUNT; i++)
	{
		if(sSocketsPool[i].sd < 0) 
			continue;
		
		Socket_Close(&sSocketsPool[i], evt);
	}
}

int Socket_IsNull(PSOCKET socket)
{
	return (!socket || socket->sd < 0);
}

